 
/**
 * @file    app_rs485.c
 * @author  Azolla (1228449928@qq.com)
 * @brief   led contorl PWM
 * @version 0.1
 * @date    2022-12-06
 * 
 * @copyright Copyright (c) 2022
 * */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"

#include "esp_system.h"
#include "nvs_flash.h"
#include "driver/uart.h"

#include "esp_log.h"
#include "sdkconfig.h"
 
#include "app_uart.h"
#include "app_rs485.h"

#define TAG "app_rs485"

// Note: Some pins on target chip cannot be assigned for UART communication.
// Please refer to documentation for selected board and target to configure pins using Kconfig.
#define UART_PIN_TXD       (1)
#define UART_PIN_RXD       (2)
#define UART_PIN_RTS       (UART_PIN_NO_CHANGE)
#define UART_PIN_CTS       (UART_PIN_NO_CHANGE)
 
static const uart_port_t uart_port = UART_NUM_1;
static const uart_mode_t uart_mode = UART_MODE_RS485_HALF_DUPLEX;
 
#define UART_BUFF_SIZE     (256)
 
// Timeout threshold for UART = number of symbols (~10 tics) with unchanged state on receive pin
#define UART_READ_TOUT     (5) // 3.5T * 8 = 28 ticks, TOUT=3 -> ~24..33 ticks

//================================================================================================= 
//================================================================================================= 
static QueueHandle_t write_queue = NULL;      // 队列
// if (xQueueReceive(write_queue, &info, portMAX_DELAY) == pdTRUE) { 
// xQueueSend(write_queue, &info, (TickType_t)100);  // 发送队列数据
//  write_queue = xQueueCreate(3, sizeof(mqtt_data_t));

static uart_func_t uart_callback_func = NULL; 

static void uart_register_callback(uart_func_t callback_func)
{
    uart_callback_func = callback_func;
}

void rs485_sendto_bytes(const uint8_t *data, uint8_t len)
{
    if (uart_write_bytes(uart_port, data, len) != len) {
        // add your code to handle sending failure here
        abort();
    } else {
        ESP_ERROR_CHECK( uart_wait_tx_done(uart_port, 20) );  
    }
}

void app_rs485_write_bytes(uint8_t addr, const uint8_t *data, uint8_t len)
{
    rs485_format_t write;
    write.cmd  = RS485_CMD_WRITE; 
    write.addr = addr;
    write.len  = len;
    memcpy(write.data, data, write.len);
    xQueueSend(write_queue, &write, (TickType_t)100);  // 发送队列数据
}

uint8_t rs485_check_xor(uint8_t *data, uint8_t len)
{
    uint8_t check = 0x00;
    for (uint16_t i = 0; i < len; i++) {
        check ^= data[i];
    }
    return check;
}
 
static void uart_driver_init(void)
{
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk= UART_SCLK_APB,
        .rx_flow_ctrl_thresh = 122,
    };
 
    // Install UART driver (we don't need an event queue here)
    // In this example we don't even use a buffer for sending data.
    ESP_ERROR_CHECK(uart_driver_install(uart_port, UART_BUFF_SIZE * 2, 0, 0, NULL, 0));

    // Configure UART parameters
    ESP_ERROR_CHECK(uart_param_config(uart_port, &uart_config));
 
    // Set UART pins as per KConfig settings
    ESP_ERROR_CHECK(uart_set_pin(uart_port, UART_PIN_TXD, UART_PIN_RXD, UART_PIN_RTS, UART_PIN_CTS));

    // Set UART half duplex mode
    ESP_ERROR_CHECK(uart_set_mode(uart_port, uart_mode));

    // Set read timeout of UART TOUT feature
    ESP_ERROR_CHECK(uart_set_rx_timeout(uart_port, UART_READ_TOUT));
}

#define RS485_MASTER_ADDR  0x80     /*!< RS485主机地址 */
#define RS485_SLAVE_NUM    4        /*!< RS485从机总数 */
#define RS485_SLAVE_ADDR   0x82     /*!< RS485从机地址 */

// Read packet timeout
#if !RS485_SLAVE_ADDR
#define PACKET_READ_TICS        (100 / portTICK_RATE_MS)
#else
#define PACKET_READ_TICS        (50 / portTICK_RATE_MS)
#endif

static bool slave_conn_status[8] = { 0 };
bool app_rs485_slave_conn_status(uint8_t addr)
{
    return slave_conn_status[(addr & 0x7F)];
}

static void uart_read_task(void *arg)
{
    rs485_format_t transmit = { 0 };
    // uint8_t dest_addr = 0x81;
    
    while ( true ) {
        #if !RS485_SLAVE_ADDR
        if (xQueueReceive(write_queue, &transmit, 100) != pdTRUE) {  // idle
            transmit.cmd  = RS485_CMD_READ;
            transmit.len  = 0x01;
            transmit.addr += 1;
            transmit.data[0] += 1;
        }  
        
        if (dest_addr < 0x81 || dest_addr > 0x82) {
            dest_addr = 0x81;
        }

        transmit.head = RS485_HEAD;
        esp_log_buffer_hex("rs485->write", transmit.frame, transmit.len + RS485_FIXED_LEN);
        rs485_sendto_bytes(transmit.frame, transmit.len + RS485_FIXED_LEN); // Write data back to UART
        #endif
        // Read data from UART.
        int len = uart_read_bytes(uart_port, transmit.frame, UART_BUFF_SIZE, PACKET_READ_TICS);
        if (len > 0) {
            // ESP_LOGI(TAG, "rs485->data: %s | %d", transmit.frame, len);
            esp_log_buffer_hex("rs485->read", transmit.frame, len);
    
            #if !RS485_SLAVE_ADDR // master host
            if (transmit.cmd == RS485_CMD_WRITE) {  // write.
                if (uart_callback_func != NULL) {   
                    uart_callback_func(uart_port, transmit.frame, len);
                }
            }
            slave_conn_status[(transmit.addr & 0x7F)] = true;
            #else 
            if (transmit.cmd == RS485_CMD_WRITE) {  // write.
                if (uart_callback_func != NULL) {   
                    uart_callback_func(uart_port, transmit.frame, len);
                    transmit.cmd = RS485_CMD_READ;  
                }
            }

            xQueueReceive(write_queue, &transmit, 0);

            if (RS485_SLAVE_ADDR == transmit.addr) {
                esp_log_buffer_hex("rs485->write[0x82]", transmit.frame, transmit.len + RS485_FIXED_LEN);
                rs485_sendto_bytes(transmit.frame, transmit.len + RS485_FIXED_LEN);
            }
            #endif
        } else {  // timeout...
            slave_conn_status[(transmit.addr & 0x7F)] = false;
        } 
    }
    vTaskDelete(NULL);
}


// uart_port_t uart_port = UART_NUM_1;
void app_rs485_init(uart_func_t callback_func)
{
    write_queue = xQueueCreate(3, sizeof(rs485_format_t)); 
    uart_register_callback(callback_func);
    uart_driver_init();
    xTaskCreatePinnedToCore(uart_read_task, "rs485_task", 3 * 1024, NULL, 8, NULL, APP_CPU_NUM);   
#if 0  // test...  
    while (1) {
        vTaskDelay(500);
        static uint8_t data = 0;
        data += 5;
        app_rs485_write_bytes(RS485_SLAVE_ADDR, &data, 1);
    }
#endif    
}
